package simco.framework.gui.services;

import java.util.Map;

import simco.framework.core.ICore;
import simco.framework.gui.graph.ConnectingLine;
import simco.framework.gui.graph.Intermediate;
import simco.framework.gui.graph.ProvidedInterface;
import simco.framework.gui.graph.UsedComponent;
import simco.framework.gui.graph.base.GraphObject;
import simco.framework.gui.graph.base.GraphRectangle;
import simco.framework.gui.graph.iface.Connectable;
import simco.framework.gui.graph.supp.ComponentType;
import simco.framework.gui.services.data.GraphModel;
import simco.framework.gui.utils.Configuration;
import simco.framework.models.CompInfo;
import simco.framework.models.LinkInfo;

/**
 * Tato trida umoznuje nacitani datoveho modelu grafu
 * simulovanych komponent ze simulacniho jadra.
 * 
 * @author Matej Prokop, mythge@students.zcu.cz
 */
public class ScenarioLoader 
{
	/**
	 * Konstanta pripocitavana k ID komponenty pri 
	 * vytvareni ID jejiho rozhrani.
	 */
	public static final int INTERFACE_CONST = 1000000;
	
	/**
	 * Konstanta pripocitavana k ID komponenty pri 
	 * vytvareni ID jeji vazby k rozhrani.
	 */
	public static final int LINE_CONST = 2000000;
	
	/**
	 * Reference na jadro simulace
	 */
	private ICore frameworkCore;	
	
	/**
	 * Vytvori novy objekt nacitajici graf simulovanych
	 * komponent ze simulacniho jadra 
	 * @param frameworkCore reference na simulacni jadro
	 */
	public ScenarioLoader(ICore frameworkCore) 
	{
		this.frameworkCore = frameworkCore;
	}
	
	/**
	 * Provede nacteni grafu simulovanych komponent ze 
	 * simulacniho jadra. Nacteny graf ulozi do datoveho
	 * modelu grafu prijateho jako parametr metody.
	 * 
	 * @param model datovy model grafu, do ktereho bude
	 * nacten graf ze simulacniho jadra
	 * @return vraci novy datovy model s nactenym grafem
	 * @throws Exception vyhazuje pokud vlakno pri ziskavani
	 * komponent a vazeb v simulaci vrati chybu 
	 */
	public GraphModel loadScenario(GraphModel model) throws Exception
	{		
		// nacte komponenty a jejich vazby
		Map<Long, CompInfo> components = this.frameworkCore.getComponents();
		Map<Long, LinkInfo> links = this.frameworkCore.getLinks();
		
		// inicializuje objekt poskytujici informace o metodach komponent
		IMethodInfoProvider mip = new MethodInfoProvider(frameworkCore);
		
		// projde vsechny registrovane komponenty
		for(long key : components.keySet())
		{
			CompInfo comp = components.get(key);
			
			GraphRectangle newObject;
			switch (comp.getType()) 
			{				
				case REAL:
					newObject = new UsedComponent(comp.getId(), comp.getName(), ComponentType.REAL, mip );
					break;
				case SIMULATION:
					newObject = new UsedComponent(comp.getId(), comp.getName(), ComponentType.SIMULATION, mip );
					break;
				case INTERMEDIATE:
					newObject = new Intermediate(comp.getId(), getCompWithIntermedaite(comp.getId(), components));
					break;	
				default:
					continue;
			}			
			
			// prida komponentu do modelu grafu			
			model.addObject(newObject);
			// vytvori graficky objekt reprezentujici rozhrani komponenty
			ProvidedInterface pInterface = new ProvidedInterface(comp.getId() + INTERFACE_CONST);
			model.addObject( pInterface );			
			// vytvori vazbu mezi komponentou a jejim rozhranim
			model.addLine(ConnectingLine.providingRelation(comp.getId() + LINE_CONST, newObject, pInterface));			
		}
		
		// nastavi zobrazovaci mod vsech komponent a grafickych objektu
		for(GraphObject go : model.getGraphObjects())
		{
			go.setViewMode(Configuration.getViewMode());
		}
		
		// do modelu ulozi vsechny vazby definovane v jadru
		for(long key : links.keySet())
		{
			LinkInfo link = links.get(key);
			
			Connectable source = model.getObjectById( link.getSource() );
			// vazby nevaze pri na komponenty, ale na jejich "vytvorena" rozhrani
			Connectable target = model.getObjectById( link.getTarget() + INTERFACE_CONST );
			model.addLine(ConnectingLine.usingRelation(link.getId(), source, target));
		}		
		
		return model;
	}
	
	/**
	 * Vraci nazev realne komponenty, ke ktere je prirazen intermediate, 
	 * jeho id je prijako jako prvni parametr metody.
	 * 
	 * @param intermedaiteId id intermediate, pro krety zjistujeme jmeno 
	 * realne komponenty komponentu zjistujeme
	 * @param components mapa vsech komponenty v simulaci
	 * @return vraci nazev realne komonenty
	 */
	private String getCompWithIntermedaite(long intermedaiteId, Map<Long, CompInfo> components)
	{
		String name = "";
		
		for(long key : components.keySet())
		{
			CompInfo comp = components.get(key);
			if(comp.getIntermediateId() == intermedaiteId)
			{
				name = comp.getName();
			}
		}
		
		return name;
	}
}
